#pragma once

#include <iostream>
#include <functional>

namespace MySmartPtr{
    template<class T>
    class SmartPtr{
    public:
        SmartPtr(T* ptr = nullptr)
        :_ptr(ptr)
        {}

        ~SmartPtr()
        {
            if(_ptr){
                std::cout << "delete: " << _ptr << std::endl;
                delete _ptr;
            }
        }

        T& operator*(){
            return *_ptr;
        }

        T* operator->(){
            return _ptr;
        }

    private:
        T *_ptr;
    };


    template<typename T>
    class auto_ptr{
    public:
        auto_ptr(T* ptr = nullptr)
        :_ptr(ptr)
        {}

        ~auto_ptr(){
            if(_ptr){
                std::cout << "delete: " << _ptr << std::endl;
                delete _ptr;
            }
        }

        auto_ptr(auto_ptr<T>& a)
        :_ptr(a._ptr)            // 移交管理权
        {
            a._ptr = nullptr;    // 原对象置为空
        }

        auto_ptr<T>& operator=(auto_ptr<T>& a){
            if (_ptr != a._ptr){
                _ptr = a._ptr;        // 移交管理权
                a._ptr = nullptr;     // 原对象置为空
            }
            return *this;
        }

        T& operator*(){
            return *_ptr;
        }

        T* operator->(){
            return _ptr;
        }

    private:
        T *_ptr;
    };


    template<typename T>
    class unique_ptr{
    public:
        unique_ptr(T* ptr = nullptr)
        :_ptr(ptr)
        {}

        ~unique_ptr(){
            if(_ptr){
                std::cout << "delete: " << _ptr << std::endl;
                delete _ptr;
            }
        }

        unique_ptr(unique_ptr<T> &u) = delete;                     // 删除拷贝构造
        unique_ptr<T> &operator=(unique_ptr<T> &u) = delete;       // 删除拷贝赋值

        // 移动构造
        unique_ptr(unique_ptr<T> &&u)
        :_ptr(u._ptr)                 
        {
            u._ptr = nullptr;
        }

        // 移动赋值
        unique_ptr<T> &operator=(unique_ptr<T> &&u){
            if (_ptr != u._ptr){
                _ptr = u._ptr;
                u._ptr = nullptr;
            }
            return *this;
        }

        T& operator*(){
            return *_ptr;
        }

        T* operator->(){
            return _ptr;
        }

    private:
        T *_ptr;
    };


    template<typename T>
    class shared_ptr{
    public:
        shared_ptr(T* ptr = nullptr)
        : _ptr(ptr)
        , _count(new int(1))
        {}

        // 定制删除器构造
        template<typename D>
        shared_ptr(T *ptr, D del) 
        : _ptr(ptr)
        , _count(new int(1))
        , _del(del)
        {}

        ~shared_ptr(){
            std::cout << "~shared_ptr()" << std::endl;
            // 析构时，先将计数器减一，然后再判断是否需要释放
            if (--(*_count) == 0){
                std::cout << "delete: " << _ptr << std::endl;
                // delete _ptr;
                _del(_ptr);
                delete _count;
            }
        }

        // 拷贝构造
        shared_ptr(const shared_ptr<T> &s)
        :_ptr(s._ptr)
        ,_count(s._count)
        ,_del(s._del)        // 拷贝删除器
        {
            ++(*_count); //同一个动态计数器，当计数器发生改变，所有共享这份资源的对象都能看到
        }

        // 拷贝赋值
        shared_ptr<T> &operator=(const shared_ptr<T> &s){
            if (_ptr != s._ptr){         
                if (--(*_count) == 0){
                    // delete _ptr;
                    _del(_ptr);
                    delete _count;
                }

                _ptr = s._ptr;
                _count = s._count;
                _del = s._del;  // 拷贝删除器
                ++(*_count);
            }
            return *this;
        }

        T& operator*(){
            return *_ptr;
        }

        T* operator->(){
            return _ptr;
        }

        T* GetPtr() const {
            return _ptr;
        }

        int& use_count() const {
            return *_count;
        }

    private:
        T *_ptr;
        int *_count;
        std::function<void(T *)> _del = [](T *ptr){
            if(ptr){
                delete ptr;
            } 
        };
    };

    template<typename T>
    class weak_ptr{
    public:
        weak_ptr()
        :_ptr(nullptr)
        {}

        weak_ptr(const shared_ptr<T> &s)
        :_ptr(s.GetPtr())
        {}

        weak_ptr<T> &operator=(const shared_ptr<T> &s){
            _ptr = s.GetPtr();
            return *this;
        }

        weak_ptr<T> &operator=(const weak_ptr<T> &w){
            _ptr = w._ptr;
            return *this;
        }

        ~weak_ptr(){
            // weak_ptr 不负责删除对象，只是观察者
        }

        T& operator*(){
            return *_ptr;
        }

        T* operator->(){
            return _ptr;
        }

    private:
        T *_ptr;
    };
}

